package com.boot.adminservice.map.service.strategy;

import com.boot.adminapi.map.constants.MapConstants;
import com.boot.adminservice.map.domain.dto.SysRegionDTO;
import com.boot.adminservice.map.domain.entity.SysRegion;
import com.boot.adminservice.map.mapper.RegionMapper;
import com.boot.commoncache.utils.CacheUtil;
import com.boot.commoncore.utils.BeanCopyUtil;
import com.boot.commonredis.service.RedisService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.benmanes.caffeine.cache.Cache;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 * 获取城市列表具体策略:缓存预热(服务启动前,在Service的实现类实施),直接查两级缓存(本地缓存+Redis)
 */
@Component("fromL2CachePreWarmStrategy")
public class FromL2CachePreWarmStrategy implements GetCityListStrategy{

    /**
     * sys_region的mapper
     */
    @Autowired
    private RegionMapper regionMapper;
    /**
     * Redis缓存服务对象
     */
    @Autowired
    private RedisService redisService;

    /**
     * 本地缓存服务对象
     */
    @Autowired
    private Cache<String, Object> caffeineCache;

    /**
     * 数据库查询策略对象
     */
    @Autowired
    private FromL2CacheStrategy fromL2CacheStrategy;

    /**
     * 城市列表查询 V4 缓存预热方案
     * @return 缓存列表数据
     */
    @Override
    public List<SysRegionDTO> getCityList() {
        //缓存预热后使用查询两级缓存的方案大概率就缓存命中(解决缓存冷启动问题)
        return fromL2CacheStrategy.getCityList();
    }

    /**
     * 按首字母A-Z分类的城市列表查询 V4 缓存预热方案
     * @return 缓存列表数据
     */
    @Override
    public Map<String, List<SysRegionDTO>> getCityPyList() {
        //缓存预热后使用查询两级缓存的方案大概率就缓存命中(解决缓存冷启动问题)
        return fromL2CacheStrategy.getCityPyList();
    }
    /**
     * 缓存预热:服务启动成功前就执行
     * 如果要使用方案1、2、3需要将@PostConstruct注释(不需要缓存预热)
     */
    @PostConstruct
    public void initCityMap() {
        // 1 直接先查数据库
        List<SysRegion> list = regionMapper.selectAllRegion();
        // 2 在服务启动期间，缓存城市列表
        loadCityInfo(list);
        // 3 在服务启动期间，缓存按A-Z分类的城市列表
        loadCityPinyinInfo(list);
    }

    /**
     * 缓存城市信息(两级缓存)
     */
    private void loadCityInfo(List<SysRegion> list) {
        // 1 查询区域信息
        List<SysRegionDTO> result = new ArrayList<>();
        // 2 对象转换
        for (SysRegion sysRegion : list) {
            if (sysRegion.getLevel().equals(MapConstants.CITY_LEVEL)) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                result.add(sysRegionDTO);
            }
        }
        // 3 设置缓存(防止缓存击穿,不设置过期时间)
        CacheUtil.setL2CacheNoExpire(redisService, MapConstants.CACHE_MAP_CITY_KEY, result, caffeineCache);
    }
    /**
     * 初始化A-Z归类城市列表缓存内容
     */
    private void loadCityPinyinInfo(List<SysRegion> list) {
        // 1 先拿城市，再封装结果
        Map<String, List<SysRegionDTO>> map = new TreeMap<>();
        for (SysRegion sysRegion : list) {
            if (sysRegion.getLevel().equals(MapConstants.CITY_LEVEL)) {
                SysRegionDTO sysRegionDTO = new SysRegionDTO();
                BeanCopyUtil.copyProperties(sysRegion, sysRegionDTO);
                // 2 首字母大写拿出来
                String firstChar = sysRegionDTO.getPinyin().toUpperCase().substring(0 ,1);
                // 3 首字母出现过
                if (map.containsKey(firstChar)) {
                    map.get(firstChar).add(sysRegionDTO);
                } else {
                    List<SysRegionDTO> regionDTOList = new ArrayList<>();
                    regionDTOList.add(sysRegionDTO);
                    map.put(firstChar, regionDTOList);
                }
            }
        }
        // 4 构建缓存
        CacheUtil.setL2CacheNoExpire(redisService, MapConstants.CACHE_MAP_CITY_PINYIN_KEY, map, caffeineCache);
    }
}
